<?php
/*
Plugin Name: WPMU Content Filter
Plugin URI: 
Description: Provides content filtering and notifications for black and gray listed items
Author: Rob Zienert @ Purple, Rock, Scissors
Author URI: http://purplerockscissors.com

Modified & Opensourced by: Josh Brower, http://DefensiveDepth.com, Josh@DefensiveDepth.com
Licensed under GPLv2

@todo Add feedback messages to the post page when a post is drafted. This may need to be done by creating
javascript that will query an ajax page looking for a message inside of session and dropping it into
the correct section of the page. Jank hook, but as far as I can tell the only way.
 */

add_action('admin_head', 'cf_install');
add_action('admin_menu', 'cf_admin_pages');
add_action('save_post', 'cf_filter', 10, 2);
add_filter('query_vars', 'cf_qvars');
add_action('template_redirect', 'cf_report_dispatch');

$cfFilterExecuted = false;
$cfTableName = 'wp_cf';


/**
 * Unforunately, using *_option functions offered by WordPress is blog-specific, so upon installation
 * of a new blog, the filters were being reset. It's a shame this installer needs to be created as
 * it creates unnecessary additional queries on every request...
 * 
 * This is hooked on admin_head so it's not run even when surfing the site. It's not the most ideal
 * solution, however no admin_init hook exists so this is the dirty second-best option. I guess it
 * could be run on a save post, but I like the idea that the filter itself is the first plugin run
 * on that hook.
 *
 */
function cf_install()
{
    global $wpdb, $cfTableName;
    
    if ($wpdb->get_var("SHOW TABLES LIKE '{$cfTableName}'") != $cfTableName) {
        $sql = "CREATE TABLE {$cfTableName} (
            id int(11) unsigned auto_increment,
            value varchar(50) default null,
            type enum('block', 'alert') default 'block',
            examples text null,
            suggestions text null,
            PRIMARY KEY (`id`)
        );";
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
    }
    
    $tableName = $cfTableName . '_exceptions';
    if ($wpdb->get_var("SHOW TABLES LIKE '{$tableName}'") != $tableName) {
        $sql = "CREATE TABLE {$tableName} (
            `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
            `keyword_id` INT( 11 ) UNSIGNED NULL ,
            `value` VARCHAR( 50 ) NULL ,
            PRIMARY KEY ( `id` )
        )";
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
    }
    
    $tableName = $cfTableName . '_notify';
    if ($wpdb->get_var("SHOW TABLES LIKE '{$tableName}'") != $tableName) {
        $sql = "CREATE TABLE {$tableName} (
            id int(11) unsigned auto_increment,
            value text null,
            PRIMARY KEY (`id`)
        );";
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
        
        $wpdb->query("INSERT INTO {$cfTableName}_notify (value) VALUES (NULL)");
    }
}

/**
 * The heavy lifter of the plugin. Searches for catch words in provided text; but does not actually
 * do anything with it. Emails, publish-overrides and the like are handled by other functions in
 * this file.
 *
 */
function cf_filter()
{
    global $cfFilterExecuted;
    
    $post = func_get_arg(1);
    
    $run_anyways = func_num_args() > 2 ? func_get_arg(2) : false;
    
    $f = func_num_args();
    
    if ($post->post_title == 'tmptitle')
        return;
        

        
    if (false === $cfFilterExecuted && ($run_anyways || (!wp_is_post_revision($post) && !wp_is_post_autosave($post))) ) {
        $cfFilterExecuted = true;
        $content = $post->post_title . "\n\n" . $post->post_content;
        
        $blockList = _cf_get_list('block');
        $alertList = _cf_get_list('alert');
        
        $embedClass = new stdClass();
        $embedClass->value = '<embed';
        
        $imgClass = new stdClass();
        $imgClass->value = '<img';
        
        $codeList  = array(
            0 => $embedClass
        );

        $caughtBlockList = array();
        $caughtAlertList = array();
        $caughtCodeList = array();
        
        $failure = false;
        
        $originalContent = $content;
        
        foreach (array('block', 'alert', 'code') as $list) {
            $catchVariable = 'caught' . ucfirst($list) . 'List';
            $listVariable  = $list . 'List';
            
            foreach ($$listVariable as $listWord) {
                $content = $originalContent;
                
                $word = trim($listWord->value);
                
                if ($listVariable != 'codeList') {
                    $content = strip_tags($content);
                }
                
                if (cf_match_word($word, $content)) {
                    if ($listVariable != 'codeList') {
                        $exceptions = cf_get_keyword_exceptions($listWord->id);
                        if (cf_word_context_has_exception($exceptions, $content)) {
                            continue;
                        }
                    }
                    
                    $failure = true;
                    array_push($$catchVariable, $listWord);
                }
            }
        }
        
        
        
        // Reassign the content to an unstripped version so that image and embed tags still get
        // properly highlighted.
        $content = $originalContent;

        if ($failure) {
        
            $total_failure = false;
            
            if (!empty($caughtBlockList)) {
                $total_failure = true;
                cf_revert_publish($post);
            }
            
            
            // only do this with auto-save
            if ($_POST['action'] == 'autosave') {
                
                $blocked_list = array();
                
                foreach ($caughtBlockList as $b) {
                    $str = "<span style='color: red'>Blocked</span> <strong>". $b->value . "</strong>";
                    if (!empty($b->suggestions))
                        $str .= ": <span style='font-size: 10px; font-variant: italics;'>" . $b->suggestions ."</span>";
                    $blocked_list[] = $str;
                }
                
                if (!empty($blocked_list)) {
                    echo "<div style='text-align: left'>" . join('<br />', $blocked_list)."</div>";
                }
                
                $alert_list = array();
                
                foreach ($caughtAlertList as $b) {
                    $str = "<span style='color: darkorange'>Alert</span> <strong>". $b->value . "</strong>";
                    if (!empty($b->suggestions))
                        $str .= " - <span style='font-size: 10px; font-variant: italics;'>" . $b->suggestions ."</span>";
                    $alert_list[] = $str;
                    
                }
                
                if (!empty($caughtAlertList)) {
                
                    echo "<div style='text-align: left; '>" . join('<br />', $alert_list) . "</div>";
                }
                
                exit;
                
            } else { 
            
                $guid = empty($post->guid) ? get_permalink($post->ID) : $post->guid;
                
                $emailCopy = cf_highlight_catch_words($caughtBlockList, $caughtAlertList, $caughtCodeList, $content);
                cf_send_notification($emailCopy, $caughtBlockList, $caughtAlertList, $caughtCodeList, $guid, $post->ID);
                cf_send_user_notification($emailCopy, $caughtBlockList, $caughtAlertList, $caughtCodeList, $guid, $post->ID);
                
            
			}
			
			return $total_failure;
			
        }
    }
}

/**
 * Reverts a post from published status
 *
 * @param object $post
 */
function cf_revert_publish($post)
{
    $post->post_status = 'draft';
    wp_update_post($post);
}

/**
 * Sends an email notification to all subscribers to the notification list, as configured by the
 * admin.
 *
 * @param string $copy
 * @param array $blacklist
 * @param array $graylist
 * @param string $guid
 */
function cf_send_notification($copy, array $blockList, array $alertList, array $code, $guid, $postid)
{
    global $userdata;
    
    $recipients = cf_get_notification_recipients();
    
    $to = current($recipients); unset($recipients[0]);
    $headers = 'cc:' . implode(',', $recipients) . "\r\n";
    
    $message = 'This an automated message notifying you that a blog post has been created which';
    $message .= ' contained the following catch words listed below. A copy of the blog post with the';
    $message .= ' words highlighted has also been included for your review.';
	$message .= "\n\nTip: You can reduce the number of Content Violation or Alert e-mails you receive by using \"Preview\" while editing a post and click \"Publish\" only once.";
    
    $wordFormat = "- \"%s\": %s\n";
    
    foreach (array('blockList', 'alertList', 'code') as $list) {
        if (count($$list) > 0) {
            $type = substr($list, 0, 5);
            $message .= "\n\nCaught {$type} words:\n";
            foreach ($$list as $word) {
                if ($list === 'code') {
                    switch ($word->value) {
                        case '<img':
                            $word->value = 'Use of an image';
                            break;
                            
                        case '<embed':
                            $word->value = 'Use of flash / video';
                            break;
                            
                        case 'default':
                            $word->value = 'Use of illegal code';
                            break;
                    }
                }
                
                $message .= sprintf($wordFormat, $word->value, empty($word->suggestions) ? 'No suggestions' : $word->suggestions);
            }
        }
    }
    
    $message .= "\n\n---- User Information ----\n\n";
    $message .= "Name: " . $userdata->user_nicename;
    $message .= "\nEmail: " . $userdata->user_email;
    
    $message .= "\n\n\n---- Blog Post ----\n";
    //$message .= "Blog Post: {$guid}\n\n\n";
    $message .= "Edit the post: $BaseURL" .$userdata->user_login. "/wp-admin/post.php?action=edit&post=$postid\n\n\n";

    $message .= $copy;

    $message = wordwrap($message, 80);

	$subject = 'Blog Post Content Violation';
	if (empty($blockList)) {
		$subject = 'Blog Post Alert';
	}
	
	$headers .= 'From: Your Organization <YourEmail@gmail.com>' . "\r\n";
   

    wp_mail($to, $subject, $message, $headers);
}

/**
 * Sends a notification email to the offending user of content violations.
 *
 * @param string $copy
 * @param array $blacklist
 * @param array $graylist
 * @param array $code
 * @param boolean $guid
 */
function cf_send_user_notification($copy, array $blockList, array $alertList, array $code, $guid, $postid)
{
    global $userdata;

    $message = 'This is an automated message notifying you that a blog post you created contained';
    $message .= ' the following catch words which are potentially not allowed. A copy of the blog post';
    $message .= ' with the words highlighted has also been included for your review.';
	$message .= "\n\nTip: You can reduce the number of Content Violation or Alert e-mails you receive by using \"Preview\" while editing a post and click \"Publish\" only once.";
    
    $wordFormat = "- \"%s\": %s\n";
    
    foreach (array('blockList', 'alertList', 'code') as $list) {
        if (count($$list) > 0) {
            $type = substr($list, 0, 5);
            $message .= "\n\nCaught {$type} words:\n";
            foreach ($$list as $word) {
                if ($list === 'code') {
                    switch ($word->value) {
                        case '<img':
                            $word->value = 'Use of an image';
                            break;
                            
                        case '<embed':
                            $word->value = 'Use of flash / video';
                            break;
                            
                        case 'default':
                            $word->value = 'Use of illegal code';
                            break;
                    }
                    $message = "- {$word}\n";
                    continue;
                }
                
                $message .= sprintf($wordFormat, $word->value, empty($word->suggestions) ? 'No suggestions' : $word->suggestions);
            }
        }
    }
    
    $message .= "\n\n\n---- Blog Post ----\n";
    
    $message .= "Edit the post: $BaseURL" .$userdata->user_login. "/wp-admin/post.php?action=edit&post=$postid\n\n\n";
    $message .= $copy;
    
    $message = wordwrap($message, 80);
    
    $to = $userdata->user_email;
    
	$subject = 'Blog Post Content Violation';
	if (empty($blockList)) {
		$subject = 'Blog Post Alert';
	}
	
	$headers = 'From: Your Organization <YourEmail@gmail.com>' . "\r\n";

    wp_mail($to, $subject, $message, $headers);
}

/**
 * Highlights all caught words with asterisks and sends it back.
 *
 * @param array $blacklist
 * @param array $graylist
 * @param string $content
 * @return string
 */
function cf_highlight_catch_words(array $blacklist, array $graylist, array $code, $content)
{
    $words = $blacklist + $graylist + $code;
    $words = cf_unique_words($words);
    array_walk($words, 'trim');
    
	$content = strip_tags($content);

    foreach ($words as $word) {
        preg_quote($word, '/');
		
        $content = preg_replace("/{$word}/i", "***{$word}***", $content);
    }
    
    return $content;
}

/**
 * Appends the content filter to the query var
 *
 * @param array $qvars
 * @return array
 */
function cf_qvars($qvars)
{
    $qvars[] = 'var_cf';
    return $qvars;
}

/**
 * Handles sending report email notifications. Will not send if the reporter email is not valid.
 *
 */
function cf_report_dispatch()
{
    $command = get_query_var('var_cf');
    
    if (empty($command)) {
        return;
    }
    
    if ($command !== 'report') {
        die('Error! Invalid content filter action');
    }
    
    if (!isset($_POST['reporter'])) {
        die('No email address provided!');
    }
    
    $email = $_POST['reporter'];
    
    // Relatively decent email validator: Certainly no Zend_Validate_EmailAddress.
    if (preg_match("/^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/" , $email)) {
    
        $message =<<<EOM
Thank you for submitting this content for abuse.

We have been notified and will looking into the issue soon.

-- This is an automated message, please do not respond. --
EOM;
    
        @wp_mail($email, 'Content Report Verification', $message);
        
        $message =<<<EOM
A post has been reported for abusive content!

Please investigate the post at the following url and, if necessary,
take proper action:

Post: {$_SERVER['HTTP_REFERER']}
Reporter: {$email}

-- This is an automated message, please do not respond --
EOM;
    
        $recipients = cf_get_notification_recipients();
        
        $to = current($recipients); unset($recipients[0]);
        $headers = 'cc:' . implode(',', $recipients);
    
        @wp_mail($to, 'Reported Content Notification', $message, $headers);
        
        $referrer = $_SERVER['HTTP_REFERER'] . '?report=success';
        if (strpos($_SERVER['HTTP_REFERER'], '?report=')) {
            $referrer = str_replace('invalid', 'success', $_SERVER['HTTP_REFERER']);
        }
        header('Location:' . $referrer);
        exit;
    } 
    
    if (!strpos($_SERVER['HTTP_REFERER'], '?report=invalid')) {
        header('Location:' . $_SERVER['HTTP_REFERER'] . '?report=invalid');
    } else {
        header('Location:' . $_SERVER['HTTP_REFERER']);
    }
    exit;
}

/**
 * Creates the report content form that is displayed on blog posts
 *
 */
function cf_report_content_link()
{
    if (isset($_GET['report']) && $_GET['report'] == 'success') {
        echo '<p>Thank you for helping keep our site clean!</p>';
    } else {
        if (isset($_GET['report']) && $_GET['report'] == 'invalid') {
            echo '<p>We could not send your content report: Invalid email address.</p>';
        }
?>
    <form action="/?var_cf=report" method="post">
        <p>
            Your Email: <input type="text" id="reporter" name="reporter" />
            <input type="submit" name="submit" value="Report Content" />
        </p>
    </form>
<?php
    }
}

/**
 * Adds necessary configuration pages to the MU admin menu.
 *
 */
function cf_admin_pages()
{
    if (function_exists('is_admin')) {
        if (is_admin()) {
            add_submenu_page('wpmu-admin.php', __('Content Filter'), __('Content Filter'), 
                8, __FILE__, 'cf_admin_page_dispatch');
        }
    }
}

/**
 * Updates content filter options. It's a quasi-field map so that the database field names are not
 * exposed to people trying to figure out the database field names. Basic protection layer but it
 * makes me feel better.
 *
 */
function cf_update_options()
{
    if (!is_admin()) {
        die(__('Is someone cheating?867'));
    }
    
    trigger_error('Deprecated with new upgrade', E_USER_ERROR);
    
    $fields = array('cf_blacklist', 'cf_graylist', 'cf_notify');
    foreach ($fields as $field) {
        $postValue = $_POST[$field];
        $metaName = end(explode('_', $field));
        _cf_save_option($metaName, $postValue);
    }
    
    cf_flash_message(__('Options successfully updated'));
}

/**
 * Searches for listed exceptions in the content
 *
 * @param array $exceptions
 * @param string $content
 * @return boolean
 */
function cf_word_context_has_exception($exceptions, $content)
{
    if (empty($exceptions)) {
        return false;
    }
    
    foreach ($exceptions as $exception) {
        if (cf_match_word($exception->value, $content)) {
            return true;
        }
    }
    
    return false;
}

function cf_get_keyword_exceptions($keywordId)
{
    global $wpdb, $cfTableName;

    return $wpdb->get_results("SELECT * FROM {$cfTableName}_exceptions WHERE keyword_id = {$keywordId}");
}

/**
 * Filters the combined caught words into a single array with all unique values
 *
 * @param array $array
 * @return array
 */
function cf_unique_words($array) 
{
    $result = array();
    foreach ($array as $element) {
        $result[] = $element->value;
    }
    return array_unique($result);
}

/**
 * The generic match regular expression used by the script; returns true if a match is found
 *
 * @param string $word
 * @param string $content
 * @return boolean
 */
function cf_match_word($word, $content)
{
    $quoted = preg_quote($word, '/');
    $pattern = "/\b({$quoted})\b/i";
    return preg_match($pattern, $content);
}

/**
 * Returns all email addresses in the recipients list for content manager notifications
 *
 * @return array
 */
function cf_get_notification_recipients()
{
    global $wpdb, $cfTableName;
    
    $recipients = $wpdb->get_var("SELECT value FROM {$cfTableName}_notify");
    $recipients = explode(',', $recipients);
    array_walk($recipients, 'trim');
    return $recipients;
}

/**
 * Wannabe private function for retrieving meta values from the database
 *
 * @param string $metaName
 * @return string
 */
function _cf_get_option($key)
{
    global $wpdb, $cfTableName;
    
    trigger_error('Deprecated with new upgrade', E_USER_ERROR);
    
    return $wpdb->get_var($wpdb->prepare("SELECT meta_value FROM {$cfTableName} WHERE meta_name = %s", $key));
}

/**
 * Returns all keyword items
 *
 * @return array
 */
function _cf_get_all()
{
    global $wpdb, $cfTableName;
    
    $listItems = $wpdb->get_results("SELECT * FROM {$$cfTableName}");
    return $listItems;
}

/**
 * Returns all keywords under a specific list
 *
 * @param string $type
 * @return array
 */
function _cf_get_list($type)
{
    global $wpdb, $cfTableName;
    
    if (!in_array($type, array('block', 'alert'))) {
        die('Invalid content filter list type');
    }
    
    $listItems = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$cfTableName} WHERE type = %s", $type));
    return $listItems;
}

/**
 * Wannabe private function for saving meta values into the database. Has a whitelist key array so
 * fishy behavior is kept to a little more of a minimum.
 *
 * @param string $key
 * @param string $value
 * @return boolean
 */
function _cf_save_option($key, $value)
{
    global $wpdb, $cfTableName;
    
    trigger_error('Deprecated with new upgrade', E_USER_ERROR);
    
    $whitelistKeys = array('blacklist', 'graylist', 'notify');
    if (!in_array($key, $whitelistKeys)) {
        return false;
    }
    
    $wpdb->query($wpdb->prepare("UPDATE {$cfTableName} SET meta_value = %s WHERE meta_name = %s", $value, $key));
    return true;
}

/**
 * Because of some mysterious WordPress voodoo, wp_is_post_autosave doesn't work; and wp_is_post_revision
 * is equally unreliable, this method exists to double check if a post should go through the filtering
 * motions. We obviously don't care about revisions or autosaves, so notifications should never be
 * sent from these post types.
 *
 * @param stdObject $post
 * @return boolean
 */
function validate_post_is_filterable($post)
{
    $pass = true;
    if ($post->post_type == 'revision') {
        $pass = false;
    }
    
    return $pass;
}

/**
 * Renders the content filter configuration page
 *
 */
function cf_admin_page_dispatch()
{
    global $wpdb, $cfTableName;
    
    $validPages = array(
        'index',
        'add',
        'edit',
        'delete',
        'notify',
    );
    
    switch ($_GET['subpage']) {
        case 'add':
            _cf_admin_page_add();
            break;
        
        case 'edit':
            _cf_admin_page_edit();
            break;
            
        case 'delete':
            _cf_admin_page_delete();
            break;
            
        case 'notify':
            _cf_admin_page_notify();
            break;
            
        case 'addexception':
            _cf_admin_page_addexception();
            break;
            
        case 'editexception':
            _cf_admin_page_editexception();
            break;
            
        case 'deleteexception':
            _cf_admin_page_deleteexception();
            break;
            
        default:
            _cf_admin_page_index();
            break;
    }
    
    return;
    
    if (isset($_POST['update_options'])) {
        cf_update_options();
    }
?>
<h2><?php echo __('Content Filter Admin') ?></h2>
<p>
    This provides configuration options for the content filter used throughout the Wordpress Mu 
    installation. <em>All words/phrases must be comma-delimited</em>.
</p>
<form method="post" action="wpmu-admin.php?page=var_cf.php">
<?php if (function_exists('wp_nonce_field')) wp_nonce_field('update-options'); ?>
    <table class="form-table">
        <tr>
            <th><label for="cf_blacklist"><?php _e('Blacklist') ?></label></th>
            <td><textarea name="cf_blacklist" id="cf_blacklist" rows="5" cols="50"><?php echo _cf_get_option('blacklist') ?></textarea></td>
        </tr>
        <tr>
            <th><label for="cf_graylist"><?php _e('Graylist') ?></label></th>
            <td><textarea name="cf_graylist" id="cf_graylist" rows="5" cols="50"><?php echo _cf_get_option('graylist') ?></textarea></td>
        </tr>
        <tr>
            <th><label for="cf_notify"><?php _e('Notify List') ?> <small>(Must be valid e-mail addresses)</small></label></th>
            <td><textarea name="cf_notify" id="cf_notify" rows="5" cols="50"><?php echo _cf_get_option('notify') ?></textarea></td>
        </tr>
    </table>
    <input type="hidden" name="action" value="update" />
    <p class="submit">
        <input type="submit" name="update_options" id="update_options" class="button-primary" value="<?php _e('Save Changes') ?>" />
    </p>
</form>
<?php
}

/**
 * Add new content filter keyword
 *
 */
function _cf_admin_page_add()
{
    global $wpdb, $cfTableName;
    
    _cf_admin_page_header();
    
    if (isset($_POST['save'])) {
        $values = array(
            'value' => $_POST['value'],
            'type' => $_POST['type'],
            'examples' => $_POST['examples'],
            'suggestions' => $_POST['suggestions'],
        );
        foreach ($values as &$value) {
            $value = stripslashes($value);
        }
        
        $wpdb->insert($cfTableName, $values);
        
        echo '<p>Successfully added new keyword</p>';
    }
    
?>
<h3>Add New Keyword</h3>
<form action="<?= _cf_admin_url('add') ?>" method="post">
    <table class="form-table">
        <tr>
            <th><label for="value">Value</label></th>
            <td><input type="text" name="value" id="value" /><br/>
            This is case-insensitive.</td>
        </tr>
        <tr>
            <th><label for="type">Type</label></th>
            <td>
                Block: <input type="radio" name="type" id="type_block" value="block" /><br/>
                Alert: <input type="radio" name="type" id="type_alert" value="alert" />
            </td>
        </tr>
        <tr>
            <th><label for="examples">Examples</label></th>
            <td><textarea name="examples" id="examples" rows="3" cols="40"></textarea></td>
        </tr>
        <tr>
            <th><label for="suggestions">Suggestions</label></th>
            <td><textarea name="suggestions" id="suggestions" rows="3" cols="40"></textarea></td>
        </tr>
        <tr>
            <th></th>
            <td><input type="submit" name="save" value="Add" /></td>
        </tr>
    </table>
</form>    
<?php
}

/**
 * Edit existing content filter keyword
 *
 */
function _cf_admin_page_edit()
{
    global $wpdb, $cfTableName;
    
    _cf_admin_page_header();
    
    if (!isset($_GET['id'])) {
        echo '<p>Error! No Keyword ID was provided.</p>';
        return;
    }
    
    if (isset($_POST['save'])) {
        $values = array(
            'value' => $_POST['value'],
            'type' => $_POST['type'],
            'examples' => $_POST['examples'],
            'suggestions' => $_POST['suggestions'],
        );
        foreach ($values as &$value) {
            $value = stripslashes($value);
        }
        
        $wpdb->update($cfTableName, $values, array('id' => $_GET['id']));
    }
    
    $keyword = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$cfTableName} WHERE id = %s", $_GET['id']));
    
?>
<h3>Keyword: "<?= $keyword->value ?>"</h3>
<form action="<?= _cf_admin_url('edit', $keyword->id) ?>" method="post">
    <table class="form-table">
        <tr>
            <th><label for="value">Value</label></th>
            <td><input type="text" name="value" id="value" value="<?= $keyword->value ?>" /><br/>
            This is case-insensitive.</td>
        </tr>
        <tr>
            <th><label for="type">Type</label></th>
            <td>
                Block: <input type="radio" name="type" id="type_block" value="block" <?php if ($keyword->type == 'block') { echo 'checked="checked"'; } ?> /><br/>
                Alert: <input type="radio" name="type" id="type_alert" value="alert" <?php if ($keyword->type == 'alert') { echo 'checked="checked"'; } ?> />
            </td>
        </tr>
        <tr>
            <th><label for="examples">Examples</label></th>
            <td><textarea name="examples" id="examples" rows="3" cols="40"><?= $keyword->examples ?></textarea></td>
        </tr>
        <tr>
            <th><label for="suggestions">Suggestions</label></th>
            <td><textarea name="suggestions" id="suggestions" rows="3" cols="40"><?= $keyword->suggestions ?></textarea></td>
        </tr>
        <tr>
            <th></th>
            <td><input type="submit" name="save" value="Save" /></td>
        </tr>
    </table>
</form>

    <h3>Exceptions</h3>
    <p><a href="<?= _cf_admin_url('addexception') ?>&keyword=<?= $_GET['id'] ?>">Add Exception</a></p>
<?php
    $exceptions = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$cfTableName}_exceptions WHERE keyword_id = %s", $_GET['id']));
?>
    <table>
    <thead>
        <tr>
            <th width="300">Exception</th>
            <th width="100" colspan="2">Actions</th>
        </tr>
    </thead>
    <tbody>
<?php
    $stripe = 0;
    foreach ($exceptions as $exception) {
?>
        <tr <?php if ($stripe % 2) { echo 'style="background:#efefef;"'; } ?>>
            <td><?= $exception->value ?></td>
            <td><a href="<?= _cf_admin_url('editexception', $exception->id) ?>&keyword=<?= $_GET['id'] ?>">Edit</a></td>
            <td><a style="color:#f00;" href="<?= _cf_admin_url('deleteexception', $exception->id) ?>&keyword=<?= $_GET['id'] ?>">Delete</a></td>
        </tr>
<?php
        ++$stripe;
    }
?>
    </tbody>
    </table>
<?php
    if (count($exceptions) == 0) {
        echo '<p>No exceptions exist for this keyword</p>';
    }
}

/**
 * Delete existing content filter keyword
 *
 */
function _cf_admin_page_delete()
{
    global $wpdb, $cfTableName;
    
    
    if (!isset($_GET['id'])) {
        echo '<p>Error! No Keyword ID was provided.</p>';
        return;
    }
    
    _cf_admin_page_header();
    
    if (!isset($_GET['confirm'])) {
        $keyword = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$cfTableName} WHERE id = %s", $_GET['id']));
?>
    <p><strong>Are you <em>sure</em> you want to delete the term "<?= $keyword->value ?>"? This cannot be undone!</strong></p>
    <p>
        <a style="color:#f00;" href="<?= _cf_admin_url('delete', $keyword->id) ?>&confirm=yes">Yes, delete this term</a> |
        <a href="<?= _cf_admin_url('index') ?>">No, nevermind. Take me back</a>
    </p>
<?php
    } else {
        $wpdb->query($wpdb->prepare("DELETE FROM {$cfTableName} WHERE id = %s", $_GET['id']));
        
        echo '<p>Success! The keyword has been successfully deleted.</p>';
    }
}

/**
 * Add term exception
 *
 */
function _cf_admin_page_addexception()
{
    global $wpdb, $cfTableName;
    
    $tableName = $cfTableName . '_exceptions';

    _cf_admin_page_header();
    
    if (!isset($_GET['keyword'])) {
        echo '<p>Error! No Keyword ID was provided.</p>';
        return;
    }
    
    if (isset($_POST['save'])) {
        $values = array(
            'keyword_id' => $_GET['keyword'],
            'value' => $_POST['value'],
        );
        foreach ($values as &$value) {
            $value = stripslashes($value);
        }
        
        $wpdb->insert($tableName, $values);
        echo '<p>Success! Added new exception. <a href="' . _cf_admin_url('edit', $_GET['keyword']) . '">Go back</a> or add another</p>';
    }
?>
<h3>Add New Exception</h3>
<form action="<?= _cf_admin_url('addexception') ?>&keyword=<?= $_GET['keyword'] ?>" method="post">
    <table class="form-table">
        <tr>
            <th><label for="value">Value</label></th>
            <td><input type="text" name="value" id="value" /><br/>
            This is case-insensitive.</td>
        </tr>
        <tr>
            <th></th>
            <td><input type="submit" name="save" value="Save" /></td>
        </tr>
    </table>
</form>
<?php
}

/**
 * Edit term exception
 *
 */
function _cf_admin_page_editexception()
{
    global $wpdb, $cfTableName;
    
    $tableName = $cfTableName . '_exceptions';

    _cf_admin_page_header();
    
    if (!isset($_GET['keyword'])) {
        echo '<p>Error! No Keyword ID was provided.</p>';
        return;
    }
    
    if (!isset($_GET['id'])) {
        echo '<p>Error! No Exception ID was provided.</p>';
        return;
    }
    
    if (isset($_POST['save'])) {
        $values = array(
            'value' => $_POST['value'],
        );
        foreach ($values as &$value) {
            $value = stripslashes($value);
        }
        
        $wpdb->update($tableName, $values, array('id' => $_GET['id']));
    }
    
    $exception = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$tableName} WHERE id = %s", $_GET['id']));
?>
<h3>Edit Exception: "<?= $exception->value ?>"</h3>
<form action="<?= _cf_admin_url('addexception', $_GET['id']) ?>&keyword=<?= $_GET['keyword'] ?>" method="post">
    <table class="form-table">
        <tr>
            <th><label for="value">Value</label></th>
            <td><input type="text" name="value" id="value" value="<?= $exception->value ?>" /><br/>
            This is case-insensitive.</td>
        </tr>
        <tr>
            <th></th>
            <td><input type="submit" name="save" value="Save" /></td>
        </tr>
    </table>
</form>
<?php
}

/**
 * Delete term exception
 *
 */
function _cf_admin_page_deleteexception()
{
    global $wpdb, $cfTableName;
    
    if (!isset($_GET['id'])) {
        echo '<p>Error! No Exception ID was provided.</p>';
        return;
    }
    
    _cf_admin_page_header();
    
    if (!isset($_GET['confirm'])) {
        $exception = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$cfTableName}_exceptions WHERE id = %s", $_GET['id']));
?>
    <p><strong>Are you <em>sure</em> you want to delete the exception "<?= $exception->value ?>"? This cannot be undone!</strong></p>
    <p>
        <a style="color:#f00;" href="<?= _cf_admin_url('deleteexception', $exception->id) ?>&keyword=<?= $_GET['keyword'] ?>&confirm=yes">Yes, delete this exception</a> |
        <a href="<?= _cf_admin_url('index') ?>">No, nevermind. Take me back</a>
    </p>
<?php
    } else {
        $wpdb->query($wpdb->prepare("DELETE FROM {$cfTableName}_exceptions WHERE id = %s", $_GET['id']));
        
        echo '<p>Success! The exception has been successfully deleted. <a href="' . _cf_admin_url('edit', $_GET['keyword']) . '">Go back to keyword</a>.</p>';
    }
}

/**
 * Modify the content filter notify list
 *
 */
function _cf_admin_page_notify()
{
    global $wpdb, $cfTableName;
    
    _cf_admin_page_header();
    
    if (isset($_POST['save'])) {
        $values = array(
            'value' => stripslashes($_POST['value']),
        );
        $wpdb->update($cfTableName . '_notify', $values, array('id' => 1));
        
        echo '<p>Successfully updated the notify list</p>';
    }
    
    $notify = $wpdb->get_var("SELECT value FROM {$cfTableName}_notify WHERE id = 1");
?>
<h3>Modify Content Manager Notify List</h3>
<form action="<?= _cf_admin_url('notify') ?>" method="post">
    <table class="form-table">
        <tr>
            <th><label for="value">Value</label></th>
            <td><textarea name="value" id="value" rows="4" cols="40"><?= $notify ?></textarea></td>
        </tr>
        <tr>
            <th></th>
            <td><input type="submit" name="save" value="Save" /></td>
        </tr>
    </table>
</form>
<?php
}

/**
 * Index page for the content filter admin
 *
 */
function _cf_admin_page_index()
{
    global $wpdb, $cfTableName;
    
    _cf_admin_page_header();
    
    $terms = $wpdb->get_results("SELECT * FROM {$cfTableName} ORDER BY value ASC");
?>
<table width="800">
<thead>
    <tr>
        <th>Keyword</th>
        <th width="100">Type</th>
        <th>Suggestions</th>
        <th width="200" colspan="2">Actions</th>
    </tr>
</thead>
<tbody>
<?php
    $stripe = 0;
    foreach ($terms as $term) {
?>
    <tr style="border-bottom:1px #ccc solid;<?php if ($stripe % 2) { echo 'background-color:#efefef;'; } ?>">
        <td><?= $term->value ?></td>
        <td><?= $term->type ?></td>
        <td><?= $term->suggestions ?></td>
        <td><a href="<?= _cf_admin_url('edit', $term->id) ?>" title="Edit term">Edit</a></td>
        <td><a style="color:#f00;" href="<?= _cf_admin_url('delete', $term->id) ?>" title="Delete term">Delete</a></td>
    </tr>
<?php
        ++$stripe;
    }
?>
</tbody>
</table>
<?php
}

/**
 * Admin menu
 *
 */
function _cf_admin_page_header()
{
    echo '<h2>Content Filter</h2>';
    
    $pages = array(
        'index' => 'Home',
        'add' => 'Add New Keyword',
        'notify' => 'Modify Content Manager List',
    );
    
    $xhtml = '<ul>';
    foreach ($pages as $page => $label) {
        $xhtml .= '<li><a href="' . _cf_admin_url($page) . '">' . $label . '</a></li>';
    }
    
    $xhtml .= '</ul>';
    echo $xhtml;
}

function _cf_admin_url($subpage, $id = null)
{
    $url = PATH_CURRENT_SITE . "wp-admin/wpmu-admin.php?page=var_cf.php&subpage={$subpage}";
    if (null !== $id) {
        $url .= '&id=' . $id;
    }
    return $url;
}

/**
 * Used for returning messages to the user.
 * 
 * @todo Make it work with sessions...
 *
 * @param null|string $message
 * @param boolean $success
 */
function cf_flash_message($message = null, $success = true)
{
    if (isset($_SESSION['cf_message_status'])) {
        $success = ('success' == $_SESSION['cf_message_status']);
        unset($_SESSION['cf_message_status']);
    }
    
    if ($success) {
        $color = 'rgb(150, 255, 180);';
    } else {
        $color = 'rgb(255, 150, 150);';
    }
    
    if (isset($_SESSION['cf_message'])) {
        $message = $_SESSION['cf_message'];
        unset($_SESSION['cf_message']);
    }
    
    if (!empty($message)) {
?>
    <div style="background-color:<?php echo $color ?>;padding:5px;border-bottom:1px #bbb solid;" id="message">
        <p>
            <strong><?php echo $message; ?></strong>
        </p>
    </div>
<?php
    }
}